package cn.lg.soar.core.util.easyexcel;

import cn.lg.soar.common.util.reflect.ReflectUtil;
import cn.lg.soar.core.util.ITemplateExport;
import cn.lg.soar.core.util.easyexcel.converters.LocalDateConverter;
import cn.lg.soar.core.util.easyexcel.converters.LocalDateTimeConverter;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import org.springframework.util.Assert;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

public class ExcelExport implements ITemplateExport {

    private final static LocalDateConverter LOCAL_DATE_CONVERTER = new LocalDateConverter();
    private final static LocalDateTimeConverter LOCAL_DATE_TIME_CONVERTER = new LocalDateTimeConverter();
    
    private final List<Sheet> sheets;

    public ExcelExport(List<Sheet> sheets) {
        Assert.notNull(sheets, "sheets must not be null");
        this.sheets = sheets;
    }

    public ExcelExport(Sheet sheet) {
        this(List.of(sheet));
    }

    public ExcelExport(Object data) {
        this(List.of(new Sheet(data)));
    }

    @Override
    public void export(InputStream template, OutputStream outputStream) {
        ExcelWriter excelWriter = EasyExcel
                .write(outputStream)
                .withTemplate(template)
                .registerConverter(LOCAL_DATE_CONVERTER)
                .registerConverter(LOCAL_DATE_TIME_CONVERTER)
                .build();
        for (int i = 0, len = sheets.size(); i < len; i++) {
            Sheet sheet = sheets.get(i);
            ExcelWriterSheetBuilder excelWriterSheetBuilder;
            if (sheet.sheetNo != null) {
                excelWriterSheetBuilder = EasyExcel.writerSheet(sheet.sheetNo);
            } else if (sheet.sheetName != null) {
                excelWriterSheetBuilder = EasyExcel.writerSheet(sheet.sheetName);
            } else {
                excelWriterSheetBuilder = EasyExcel.writerSheet(i);
            }
            if (sheet.cellWriteHandler != null) {
                excelWriterSheetBuilder.registerWriteHandler(sheet.cellWriteHandler);
            }
            excelWriterSheetBuilder.registerConverter(LOCAL_DATE_CONVERTER);
            excelWriterSheetBuilder.registerConverter(LOCAL_DATE_TIME_CONVERTER);
            WriteSheet writeSheet = excelWriterSheetBuilder.build();

            FillConfig fillConfig = sheet.getFillConfig();
            Object data = sheet.data;
            if (data instanceof Collection collection) {
                excelWriter.fill(collection, fillConfig, writeSheet);
            } else if (data instanceof Map map) {
                Set<? extends Map.Entry<?, ?>> entries = map.entrySet();
                for (Map.Entry<?, ?> entry : entries) {
                    if (entry.getValue() instanceof Collection<?> collection) {
                        excelWriter.fill(new FillWrapper(String.valueOf(entry.getKey()), collection), fillConfig, writeSheet);
                    }
                }
                excelWriter.fill(data, writeSheet);
            } else {
                ReflectUtil.scanField(data.getClass(), field -> {
                    try {
                        field.setAccessible(true);
                        Object object = field.get(data);
                        excelWriter.fill(new FillWrapper(field.getName(), (Collection) object), fillConfig, writeSheet);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } finally {
                        field.setAccessible(false);
                    }
                }, f -> Collection.class.isAssignableFrom(f));
                excelWriter.fill(data, writeSheet);
            }
        }
        // 导出
        excelWriter.finish();
    }

    public static class Sheet {

        /**
         * 填充的数据
         */
        private final Object data;

        private final FillConfig fillConfig;

        private Integer sheetNo;

        private String sheetName;

        private CellWriteHandler cellWriteHandler;


        public Sheet(Object data) {
            this(data, true);
        }

        public Sheet(Object data, Boolean forceNewRow) {
            this(data, FillConfig.builder().forceNewRow(forceNewRow).build());
        }

        public Sheet(Object data, FillConfig fillConfig) {
            this.data = data;
            this.fillConfig = fillConfig;
        }

        public Object getData() {
            return data;
        }

        public FillConfig getFillConfig() {
            return fillConfig;
        }

        public Integer getSheetNo() {
            return sheetNo;
        }

        public void setSheetNo(Integer sheetNo) {
            this.sheetNo = sheetNo;
        }

        public String getSheetName() {
            return sheetName;
        }

        public void setSheetName(String sheetName) {
            this.sheetName = sheetName;
        }

        public CellWriteHandler getCellWriteHandler() {
            return cellWriteHandler;
        }

        public void setCellWriteHandler(CellWriteHandler cellWriteHandler) {
            this.cellWriteHandler = cellWriteHandler;
        }

    }
}
